/*jslint white: false, eqeqeq: false, browser: true, devel: true, onevar: true,
 undef: true, nomen: true, bitwise: true, newcap: true, immed: true,
css: true maxlen: 80, plusplus: false, */
/*global google: false, $: false, window: false */
google.load('jquery', '1.4.2');
// Globals
var column = -1;
var content = 
 ['<html>\n',
  '  <head>\n',
  '    <title>My test page<\/title>\n',
  '  <\/head>\n',
  '  <body>\n',
  '    <h1>My test page<\/h1>\n',
  '    <p>I made this page for testing purposes.\n',
  '    I hope to use it to test out my web based version of vim.\n',
  '    <\/p>\n',
  '  <\/body>\n',
  '<\/html>\n'].join('');
var cursor = 0;
var marks = {};
var command;
var command_cursor = 0;
var after = null;
var modes = {NORMAL: 0,
             INSERT: 1,
             VISUAL: 2,
             COMMAND: 3,
             NORMAL_MOVE_LINE: 4,
             NORMAL_F: 5,
             NORMAL_G: 6,
             NORMAL_M: 7,
             NORMAL_Z: 8
             };
var mode = modes.NORMAL;
var filename;
var options = {};
var defaults = {}; //Currently not used.
var menu_open = false;
var menu_height;
function error(message) {
  var html = html || '';
  console.log(html);
  $('#status').addClass('error');
  $('#status').html(message);
  setTimeout(function(){$('#status').removeClass('error');}, 1000);
}
function todo() {
  console.log("todo");
  error('todo');
}
function close_intro() {
    $('#intro').css({width: $('#intro').width() + 'px',
                     height: $('#intro').height() + 'px'});
    $('#intro').css({left: 'auto',
                     bottom: ($(window).height() - $('#intro').height() - 50) +
                       'px',
                     top: 'auto'});
    $('#close_button').animate({width: 0, height: 0});
    $('#intro').animate({opacity: '0',
                         right: '0',
                         bottom: '0',
                         height: '20px',
                         width: '50px',
                         'font-size': '0px'}, {complete: function(){
      $('#intro').css({display: 'none'});
    }});
}
function open_intro() {
  $('#close_button').css({width: '', height: ''});
  $('#intro').css({ display: 'block',
                   opacity: '1',
                   bottom: '',
                   left: '',
                   height: 'auto',
                   width: 'auto',
                   top: '50px',
                   'font-size': 'inherit',
                   'max-width': '500px'});
  $('#intro').css('right', ($(window).width() - $('#intro').width()) / 2);
}
function open_settings(){
  todo();
}
function close_settings(){
  todo();
}
function open_menu() {
  $('#menu').css({display: 'block'});
  $('#menu').animate({height: menu_height + 'px', 'padding-top': '10px',
      'padding-bottom': '20px'});
  menu_open = true;
}
function close_menu() {
  $('#menu').animate({height: '0', 'padding-top': '0', 'padding-bottom': '0'},
      {complete: function() {
    // $('#menu').css({display: 'none'});
  }});
  menu_open = false;
}
function toggle_menu() {
  if (menu_open) {
    close_menu();
  } else {
    open_menu();
  }
}
function update_status(message) {
  $('#status').html(message);
  $('#status').addClass('attention');
  setTimeout(function(){$('#status').removeClass('attention');}, 100);
}
function clean(str) {
  str = str.replace(/&/g, '&amp;');
  str = str.replace(/ /g, '&nbsp;');
  str = str.replace(/</g, '&lt;');
  str = str.replace(/>/g, '&gt;');
  str = str.replace(/\n/g, '<br />');
  return str;
}
function draw() {
  var render, mode_string, render_command, front, middle, end;
  if (mode != modes.COMMAND) {
    front = content.substring(0, cursor);
    middle = content.charAt(cursor);
    if (middle == '\n') {
      middle = ' \n';
    }
    end = content.substring(cursor + 1, content.length);
    render = clean(front) + '<span id="cursor">' + clean(middle) + '<\/span>' +
      clean(end);
    $('#content').html(render);
  }
  if (mode == modes.COMMAND) {
    render_command = clean(command.substring(0, command_cursor)) + 
      '<span id="cursor">&nbsp;<\/span>' +
      clean(command.substring(command_cursor, command.length));
    $('#status').html(':' + render_command);
  }
}
function insertAtCommandCursor(character) {
  command =
    command.substring(0, command_cursor) + 
    character + 
    command.substring(command_cursor, content.length);
  command_cursor++;
}
function insertAtCursor(character) {
  content =
    content.substring(0, cursor) + 
    character + 
    content.substring(cursor, content.length);
  cursor++;
}
function moveCursorDown() {
  var col, next_break, next_next_break;
  col = column > -1 ? column :
            cursor - content.substring(0, cursor).lastIndexOf('\n');
  column = col;
  next_break = content.substring(cursor, content.length)
    .indexOf('\n') + cursor;
  next_next_break = content.substring(next_break + 1, content.length)
    .indexOf('\n') + next_break;
  cursor = next_break + Math.min(col, next_next_break - next_break);
}
function moveCursorUp() {
  var col, prev_break, this_break;
  col = cursor - content.substring(0, cursor).lastIndexOf('\n');
  prev_break = content.substring(0, cursor - col).lastIndexOf('\n');
  this_break = content.substring(0, cursor).lastIndexOf('\n');
  if (column > -1) {
    col = column;
  }
  column = col;
  cursor = prev_break + Math.min(col, this_break - prev_break);
}
function setOption(key, value) {
  // The only working option right now is syntax
  switch(key) {
    case 'syntax':
      if (options.syntax) {
        $("#content").removeClass("lang-" + options.syntax);
      }
      options.syntax = value;
      $("#content").addClass("lang-" + options.syntax);
      break;
    case 'number':
      todo();
      break;
    default: todo();
  }
}  
function process_set(cmd) {
  var optionKey, optionValue, splitCmd;
  if (/(\w+)(?:=|\:)(\w+)/.test(cmd)) {
    splitCmd = cmd.split(/(?:\:|=)/g);
    optionKey = splitCmd[0];
    optionValue = splitCmd[1];
    // In vim there is a check to see if it's a usable keyword. We will
    // probably need to check for that sometime later, but I don't
    // have anything done about that yet.
  }
  setOption(optionKey, optionValue);
}
function save() {
  if (window.localStorage) {
    window.localStorage.setItem('content',content);
  }
}
function issue_command() {
  var fn, command_pieces;
  command_pieces = $.trim(command).split(/\s+/);
  switch(command_pieces[0]) {
    case 'syntax':
      switch(command_pieces[1]) {
        case 'enable':
        case 'on':
          // Add code highlighting
          break;
        case 'off':
          // remove code highlighting
          break;
        default: error();
      }
      break;
    case 'se':
    case 'set':
      // There are a lot of things to do here.  See ':help set'
      process_set(command_pieces[1]);
      break; 
    case 'w':
    case 'wr':
    case 'wri':
    case 'writ':
    case 'write':
      if (command_pieces[1]) {
        filename = command_pieces[1];
      }
      if (filename) {
        update_status('Saving...');
        $.ajax({type:'POST', url: '/write', data: {content: content,
                                                   filename: filename},
                success: function(response){
            if (response == 'success') {
              update_status('"' + filename + '" written to server at ' + 
                (new Date()));
              $('title').html(filename + ' - VIGOR');
            } else {
              error('Problem saving to server: ' + response);
            }
          }
        });
      } else {
        error('No file name specified.');
      }
      break;
    case 'settings':
      open_settings();
      break;
    case 'about':
      console.log('about command issued');
      open_intro();
      break;
    case 'pre':
    case 'pres':
    case 'prese':
    case 'preser':
    case 'preserv':
    case 'preserve':
      save();
      update_status('File preserved.');
      break;
    case 'o':
    case 'op':
    case 'ope':
    case 'open':
      if (!command_pieces[1]) {
        error('No filename specified');
      } else {
        fn = command_pieces[1];
        update_status('Opening...');
        $.ajax({type:'GET', url: '/read', data: {filename: fn}, success: 
          function (response){
            if (response.substring(0,8) == 'success:') {
              filename = fn;
              content = response.substring(8);
              draw();
              update_status('File ' + fn + ' opened.');
            } else {
              error('File could not be opened: ' + response);
            }
          }});
      }
      break;
    default: todo();
  }
  command = '';
  command_cursor = 0;
}
function handle(e) {
  var k, c, begin, end;
  k = e.which;
  c = String.fromCharCode(k);
  // These should fall back to normal browser behavior
  if ((e.metaKey && !e.ctrlKey) ||
      (e.ctrlKey && c == 'R') ||
      (e.ctrlKey && c == 'T') ||
      (e.ctrlKey && c == 'W') ||
      (e.ctrlKey && e.shiftKey && c == 'J') ||
      (e.ctrlKey && c == 'W') ||
      (e.ctrlKey && c == 'L') ||
      (k == 18) || (k == 17) || (k == 16)) {
    return;
  } else  {
    e.preventDefault();
  }
  close_menu();
  k = e.which;
  c = String.fromCharCode(k);
  if (!e.shiftKey && k >= 65 && k <= 90) {
    c = String.fromCharCode(k + 32);
  }
  if (!e.ctrlKey && k >= 41 && k <= 126) {
    if (e.shiftKey) {
      switch (k) {
        case 48: c = ')'; break;
        case 49: c = '!'; break;
        case 50: c = '@'; break;
        case 51: c = '#'; break;
        case 52: c = '$'; break;
        case 53: c = '%'; break;
        case 54: c = '^'; break;
        case 55: c = '&'; break;
        case 56: c = '*'; break;
        case 57: c = '('; break;
      }
    }
  } else {
    switch(k) {
      case 189: if (e.shiftKey) { c = '_'; } else { c = '-'; } break;
      case 219: if (e.shiftKey) { c = '{'; } else { c = '['; } break;
      case 221: if (e.shiftKey) { c = '}'; } else { c = ']'; } break;
      case 186: if (e.shiftKey) { c = ':'; } else { c = ';'; } break;
      case 222: if (e.shiftKey) { c = '"'; } else { c = '\''; } break;
      case 188: if (e.shiftKey) { c = '<'; } else { c = ','; } break;
      case 190: if (e.shiftKey) { c = '>'; } else { c = '.'; } break;
      case 191: if (e.shiftKey) { c = '?'; } else { c = '/'; } break;
      case 220: if (e.shiftKey) { c = '|'; } else { c = '\\'; } break;
      case 187: if (e.shiftKey) { c = '+'; } else { c = '='; } break;
      case 192: if (e.shiftKey) { c = '~'; } else { c = '`'; } break;
      case 13: c = '\n'; break;
      case 9: c = '  '; break;
      case 32: c = ' '; break;
    }
  }
  switch(mode) {
    case modes.NORMAL_F:
      break;
    case modes.NORMAL_G:
      switch (c) {
        case 'g':
          cursor = 0;
          break;
        case 'j':
          todo();
          break;
        case 'k':
          todo();
          break;
      }
      break;
    case modes.NORMAL_M:
      marks[c] = cursor;
      break;
    case modes.NORMAL_Z:
      break;
    case modes.NORMAL_MOVE_LINE:
      switch (c) {
        case 'd':
          begin = content.substring(0, cursor).lastIndexOf('\n') + 1;
          end = 1 + cursor + 
            content.substring(cursor, content.length).indexOf('\n');
          after(begin, end);
          break;
        case 'j':
          begin = content.substring(0, cursor).lastIndexOf('\n') + 1;
          end = 1 + cursor + 
            content.substring(cursor, content.length).indexOf('\n');
          end = 1 + end + 
            content.substring(end, content.length).indexOf('\n');
          after(begin, end);
          break;
      }
      if (k == 27) {
        mode = modes.NORMAL;
        update_status('');
      }
      break;
    case modes.NORMAL:
      switch (c) {
        // Try to keep these in lexicographical order.
        case '$':
          cursor = -1 + cursor + 
            content.substring(cursor, content.length).indexOf('\n');
          column = 9999;
          break;
        case ':':
          mode = modes.COMMAND;
          command = '';
          command_cursor = 0;
          break;
        case 'a':
          cursor++;
          mode = modes.INSERT;
          update_status('-- INSERT --');
          break;
        case 'b':
          cursor = content.substring(0, cursor).lastIndexOf(' ');
          break;
        case 'c':
          mode = modes.NORMAL_MOVE_LINE;
          after = function(begin, end) {
            content = content.substring(0, begin) +
                      content.substring(end, content.length);
            cursor = begin;
            mode = modes.INSERT;
            update_status('-- INSERT --');
          };
          break;
        case 'd':
          mode = modes.NORMAL_MOVE_LINE;
          after = function(begin, end) {
            content = content.substring(0, begin) +
                      content.substring(end, content.length);
            cursor = begin;
            mode = modes.NORMAL;
          };
          break;
        case 'e':
          cursor += content.substring(cursor).indexOf(' ');
          break;
        case 'f':
          mode = modes.NORMAL_F;
          update_status('f');
          break;
        case 'g':
          mode = modes.NORMAL_G;
          update_status('g');
          break;
        case 'h':
          cursor--;
          column = -1;
          break;
        case 'i':
          mode = modes.INSERT;
          update_status('-- INSERT --');
          break;
        case 'j':
          moveCursorDown();
          break;
        case 'k':
          moveCursorUp();
          break;
        case 'l':
          cursor++;
          column = -1;
          break;
        case 'm':
          mode = modes.NORMAL_M;
          update_status('m');
          break;
        case 'o':
          cursor = cursor + 
            content.substring(cursor, content.length).indexOf('\n');
          insertAtCursor('\n');
          mode = modes.INSERT;
          update_status('-- INSERT --');
          column = -1;
          break;
        case 'x':
          content =
            content.substring(0, cursor) + 
            content.substring(cursor + 1, content.length);
          break;
        case 'z':
          mode = modes.NORMAL_Z;
          update_status('z');
          break;
      }
      if (content.charAt(cursor) == '\n' && mode == modes.NORMAL) {
        if (content.charAt(cursor + 1) != '\n') {
          if (c == 'h') {
            cursor++;
          } else if (c != 'k') {
            cursor--;
          }
          error();
        } else {
          cursor++;
        }
      }
      save();
      close_intro();
      break;
    case modes.INSERT:
      switch (k) {
        case 8: // Backspace
          content =
            content.substring(0, cursor - 1) + 
            content.substring(cursor, content.length);
          cursor--;
          break;
        case 37: cursor--; break;
        case 39: cursor++; break;
        case 38: moveCursorUp(); break;
        case 40: moveCursorDown(); break;
        case 27: // esc
          save();
          mode = modes.NORMAL;
          cursor--;
          update_status('');
          break;
        case 16: /* Ignore shift key */ break;
        default:
          if (e.ctrlKey && c == '[') {
            mode = modes.NORMAL;
            cursor--;
            update_status('');
            update_status('');
          } else {
            insertAtCursor(c);
          }
      }
      break;
    case modes.COMMAND:
      //console.log(k);
      switch (k) {
        case 8: // Backspace
          if (command.length === 0) {
            mode = modes.NORMAL;
            update_status('');
          }
          command =
            command.substring(0, command_cursor - 1) + 
            command.substring(command_cursor, command.length);
          command_cursor--;
          break;
        case 37: command_cursor--; break;
        case 39: command_cursor++; break;
        case 38: todo(); break;
        case 40: todo(); break;
        case 27: // esc
          mode = modes.NORMAL;
          update_status('');
          break;
        case 16: /* Ignore shift key */ break;
        case 17: /* Ignore ctrl key */ break;
        case 13:
          issue_command();
          mode = modes.NORMAL;
          break;
        default: insertAtCommandCursor(c);
      }
      break;
  }
  // Fix issues
  if (cursor > content.length) {
    cursor = content.length;
  } else if (cursor < 0 && content.length > 1) {
    cursor = 0;
  }
  console.log(cursor);
  if (content.charAt(content.length - 1) != '\n') {
    content += '\n';
  }
  draw();
}
google.setOnLoadCallback(function(){
  var newContent;
  $(document).keydown(handle);
  if (window.localStorage) {
    newContent = window.localStorage.getItem('content');
    if (newContent) {
      content = newContent;
    }
  }
  $('#close_button').click(function(e) {
    e.preventDefault();
    close_intro();
  });
  $('#menu_button').click(function(e) {
    e.preventDefault();
    toggle_menu();
  });
  // Get menu height
  $('#menu').css({left: '-1000px', top: '-1000px', display: 'block'});
  menu_height = $('#menu').height();
  console.log(menu_height);
  $('#menu').css({left: '', top: '', display: 'none', right: '10px',
      bottom: '0', height: '0'});

  $('#intro').css('right', ($(window).width() - $('#intro').width()) / 2);
  $('#intro').css('display', 'block');
  $('#content').click(function(e) { close_menu(); });
  $('#menu > li').click(function (e) {
    command = $(this).find('.right').html().substring(1);
    issue_command();
    command = '';
  });
  draw();
});
